VAR ReportMsg: PROCEDURE (msg, p0, p1, p2: ARRAY OF CHAR);
PROCEDURE Clone (s: Store): Store;
PROCEDURE New (type: TypeName): Store;
PROCEDURE GetTypeName (s: Store; VAR type: TypeName);
PROCEDURE Report (msg, p0, p1, p2: ARRAY OF CHAR);
END Stores.
Module Stores defines a data type Store, which should be used as base type of all storable extensible objects. When storing an object of an extensible type, it is necessary to store not only its contents but also its particular type. A variable of (an extension of) type Store is stored in a file. A store must implement the Internalize procedure which takes a Reader as parameter, and the Externalize procedure which takes a Writer as parameter. Readers and writers are mappers on files. The types Views.View and TextModels.Model are examples of Store extensions.
The contents of a store is stored in a file. When reading the same file by another Oberon configuration, it may occur that not all necessary modules are available in this configuration, i.e. the module which defines the store's type cannot be loaded. Yet reading such an "alien" store does not fail completely. Instead of the correct store type, an "alien" object is generated. Obviously such an alien cannot interpret the data it represents, and therefore cannot provide any special behavior. However, it may be copied and stored into another file, such that its contents on the new file are intact and consistent.
Elements are special stores which belong to a particular domain (-> 5.1 "Domains"). The elements of a domain can be externalized and internalized such that pointers among them are reconstructed correctly upon internalization. In particular, alias pointers are handled correctly: if several elements point to another element of the same domain, this element is read in only once, and all the pointers to it are rebuilt. Arbitrary graphs can be handled this way. Links to elements of other domains are severed, i.e. become NIL upon internalization. Pointers to non-element stores yield the store again, but alias pointers are not handled: if several elements (or stores) point to a store, internalization creates one new store per pointer, such that each element gets its own independent instance of the store.
Stores provides a pair of mapper types, which are used as parameters in a store's Internalize and Externalize procedures. These readers/writers use the following external (little endian) format:
BOOLEAN
1 byte (0 = FALSE, 1 = TRUE)
1 byte in the Latin-1 character set (i.e. Unicode page 0; 00X..0FFX)
LONGCHAR
2 byte in the Unicode character set (0000H..0FFFFH)
SHORTINT
1 byte (-128..127)
INTEGER
2 bytes (-32768..32767)
LONGINT
4 bytes (-2147483648..2147483647)
4 bytes IEEE format
LONGREAL
8 bytes IEEE format
4 bytes (least significant bit = element 0)
String
string in the Latin-1 character set, followed by a 00X
Long String
string in the Unicode character set, followed by a 0000H
CONST alienVersion
This value is assigned to a Reader's cause field if Reader.ReadVersion read a version outside of the specified range.
CONST alienComponent
This value can be used as cause parameter to Reader.TurnIntoAlien to indicate that the store itself could be read, but that some store contained in it is an alien. As an example, a view may turn itself into an alien if its model is an alien.
CONST inconsistentVersion
This value is assigned to a Reader's cause field if Reader.ReadVersion read a data block which has an inconsistent length, i.e. not all of its data have been read, or it has been attempted to read beyond the end of the data.
CONST inconsistentType
This value is assigned to a Reader's cause field if Reader.ReadVersion detected a change in the type extension hierarchy of the internalized type.
CONST moduleFileNotFound
This value is assigned to a Reader's cause field if Reader.ReadVersion tried to load a module defining an internalized type, and the codefile for this module couldn't be found.
CONST invalidModuleFile
This value is assigned to a Reader's cause field if Reader.ReadVersion tried to load a module defining an internalized type, and the module couldn't be loaded because it imports another module which cannot be loaded for some reason.
CONST inconsModuleVersion
This value is assigned to a Reader's cause field if Reader.ReadVersion tried to load a module defining an internalized type, and the module couldn't be loaded because its version is inconsistent with some already loaded module.
CONST typeNotFound
This value is assigned to a Reader's cause field if Reader.ReadVersion tried to internalize a non-existing type (the module was found, however).
TYPE LONGCHAR
Type for 2-byte characters in the Unicode character set.
TYPE TypeName
String type for the type name of an object.
TYPE TypePath
Array of type names.
TYPE Store
Interface
Storable extensible data types like Views.View or TextModels.Text are derived from Store.
Stores are allocated by suitable directories, e.g. Views.Directory or TextModels.Directory.
Stores are used as base types for all extensible and persistent objects.
init-: BOOLEAN
This flag indicates whether a store's basic initialization steps have been performed already. Once set, it cannot be cleared anymore.
Internalize asserts that the internalized store is not initialized, and then marks it as initialized.
Externalize asserts that the externalized store is already initialized.
A copy procedure typically asserts that the source is already initialized and that the destination is not yet initialized, and then marks it as initialized.
A directory's allocation function returns an initialized store.
Other initializations (e.g. assigning other store fields in an extension) may (but need not) be guarded by ~init, other operations may only be legal if init.
PROCEDURE (s: Store) Clone (): Store
Default
Clone allocates a new object with the same dynamic type as s. The contents of the new object are cleared (not copied!).
Clone is used before the object's contents is copied.
Clone needs not be extended in extensions of Store. However, it can be useful to extend Clone in order to narrow the result type to the actual receiver type (covariance).
Extensions should never make a super call (use the global procedure Clone instead).
Except for performance, equivalent to:
RETURN Clone(s)
result # NIL
~result.init
Type(result) = Type(s)
PROCEDURE (s: Store) Init
Initialization of a store. This procedure may be called once only.
Init is called in Internalize, in any copy procedure that an extended store may provide, and in the allocation procedures of store directories.
A store may be associated with a domain. Such an association may not be changed anymore. Not all stores support domains; for such stores InitDomain can be considered empty, except for the first two of the following precondition checks:
s.init 20
d = NIL OR d.init 21
dom(s) = NIL OR dom(s) = d 22
s supports domain
dom(s) = d
PROCEDURE (s: Store) Internalize (VAR rd: Reader)
Reads the contents of s from reader rd. Internalize must read the same (amount of) data as is written by the corresponding Externalize procedure.
Internalize is called internally.
Internalize is extended by various persistent object types, e.g. models, views, and controllers.
Extensions must make a super call first.
~s.init 20
s.init
PROCEDURE (s: Store) Externalize (VAR wr: Writer)
Write the contents of s to writer wr. Externalize must write the same (amount of) data as is read by the corresponding Internalize procedure.
Externalize ist called internally.
Externalize is extended by various persistent object types, e.g. models, views, and controllers.
Extensions must make a super call first.
s.init 20
TYPE Elem
Interface, Extension
Store readers and writers handle pointers between Elem objects of the same domain such that aliases are handled correctly. This means that elements of a domain can form an arbitrary graph, which can be written and read in again without losing its structure. Element pointers must be written/read using Writer.WriteStore and Reader.ReadStore.
domain-: Domains.Domain
The element's domain, which is initialized by the InitDomain procedure.
TYPE Reader
Reader for Oberon/L values like integers, reals, or sets. A reader contains a Files.File, to which it forwards most operations.
Readers are used in the Store.Internalize procedure.
Readers are not extended.
rider-: Files.Reader
The file rider which links a Reader to a file.
cancelled-: BOOLEAN valid during a Store.Internalize call
Tells whether the currently executing Internalize has been called by ReadVersion or TurnIntoAlien.
readAlien-: BOOLEAN
Tells whether any alien has been read since the last ConnectTo.
Connect the reader to a file. All the following operations require connected readers, i.e. rd.rider # NIL. This precondition is not checked explicitly, however. After connecting, the reader's position is at the beginning of the file.
Reads a store's type, allocates it, and then reads its contents, by calling the store's Internalize procedure. x may also be NIL, or an alien if the store's module cannot be loaded, or if internalization has been cancelled by the Internalize procedure.
If the store is an Elem which has already been read in, a pointer to the same Elem is returned instead of allocating a new one.
empty store on file
x = NIL
non-empty store on file
x # NIL
x IS Alien
x.cause # 0
x.type # ""
x.file # NIL
x.pos >= 0 beginning of store's data
x.len >= 0 length of store's data
alien store contents are on x.file in the range [x.pos .. x.pos + x.len[.
These data include only the store's contents, not its prefix
Read a version byte and return it in version. If version is not in the specified range [min .. max], the store currently being read is turned into an alien, with cause = alienVersion.
Connect the writer to a file. All the following operations require connected writers, i.e. wr.rider # NIL. This precondition is not checked explicitly, however. After connecting, the writer's position is at the end of the file.
PROCEDURE (VAR wr: Writer) WriteString (x: ARRAY OF CHAR)
Writes a 00X-terminated string.
PROCEDURE (VAR wr: Writer) WriteLString (x: ARRAY OF LONGCHAR)
Writes a 0000H-terminated string.
PROCEDURE (VAR wr: Writer) WriteStore (x: Store)
Writes the store's type and then its contents, by calling the store's Externalize procedure. x may also be NIL, or an alien.
PROCEDURE WriteVersion (version: SHORTINT)
Writes a version byte.
version >= 0 20
TYPE Alien, AlienComp, AlienElem, AlienPart, AlienPiece
These auxiliary types are used internally, to handle alien stores.
VAR ReportMsg: PROCEDURE (msg, p0, p1, p2: ARRAY OF CHAR)
Used internally.
PROCEDURE New (type: TypeName): Store
Given the name of a store type in the form "module.record", New returns an object of this type. If the defining module is not yet loaded, New tries to load it. If it cannot be loaded, if there is not enough memory, or if the module doesn't define this type, New returns NIL.
New is called internally.
result = NIL
type not available
result # NIL
type available
result has desired type
PROCEDURE Clone (s: Store): Store
Returns the clone of a store.
Clone is used internally for the default implementation of Store.Clone, and may be used to implement the Clone procedure of a store extension, instead of performing a super call and then applying a type guard
PROCEDURE GetTypeName (s: Store; VAR type: TypeName)